package com.gun3y.manager;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.gun3y.entity.Wrapper;
import com.gun3y.entity.media.WrapperDataProps;
import com.gun3y.entity.media.WrapperObjectProps;
import com.gun3y.entity.pattern.AttributePattern;
import com.gun3y.entity.pattern.FieldPattern;
import com.gun3y.entity.pattern.FieldPatternExpression;
import com.gun3y.entity.pattern.PositionPattern;
import com.gun3y.entity.pattern.TagPattern;
import com.gun3y.exception.ExceptionCode;
import com.gun3y.exception.SWException;
import com.gun3y.ts.dao.TripleStore;
import com.gun3y.ts.helper.TripleHelper;
import com.gun3y.ts.helper.WrapperSparqlHelper;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.NodeFactory;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;

@Service
public class WrapperGraphManager {

    private String graphName = "http://www.gun3y.com/ontology/wrapper";

    private TripleStore store = new TripleStore(graphName);

    public void clear() {
	this.store.clear();
    }

    public boolean exists(String wrapperName) {
	if (StringUtils.isBlank(wrapperName)) {
	    throw new SWException(ExceptionCode.InvalidInput);
	}
	return this.store.executeAskQuery(WrapperSparqlHelper.askWrapper(wrapperName));
    }

    public List<Wrapper> getAllWrappers() {
	List<Wrapper> retList = new ArrayList<Wrapper>();
	List<String> wrapperNameList = getWrapperNames();
	for (String wrapperName : wrapperNameList) {
	    Wrapper wrapper = getWrapperByName(wrapperName);
	    if (wrapper != null) {
		retList.add(wrapper);
	    }
	}
	return retList;
    }

    public Wrapper getWrapperByName(String wrapperName) {
	if (StringUtils.isBlank(wrapperName)) {
	    throw new SWException(ExceptionCode.InvalidInput);
	}

	List<QuerySolution> executeSelectQuery = this.store.executeSelectQuery(WrapperSparqlHelper.getWrapperTriples(wrapperName));

	if (executeSelectQuery == null || executeSelectQuery.isEmpty()) {
	    return null;
	}

	Wrapper wrapper = new Wrapper();
	wrapper.setName(wrapperName);
	for (QuerySolution querySolution : executeSelectQuery) {
	    // RDFNode wrapperNode = querySolution.get("wrapper");
	    RDFNode predicateNode = querySolution.get("predicate");
	    RDFNode objectNode = querySolution.get("object");
	    // System.out.println(wrapperNode.toString() + " " +
	    // predicateNode.toString() + " " + objectNode.toString());
	    if (predicateNode.isResource()) {
		Resource predicateRes = predicateNode.asResource();
		WrapperDataProps dataProp = null;
		WrapperObjectProps objectProp = null;
		try {
		    dataProp = WrapperDataProps.valueOf(predicateRes.getLocalName());
		}
		catch (IllegalArgumentException ex) {

		}
		try {
		    objectProp = WrapperObjectProps.valueOf(predicateRes.getLocalName());
		}
		catch (IllegalArgumentException ex) {

		}

		if (dataProp != null) {
		    dataProp.set(wrapper, objectNode.toString());
		}
		if (objectProp != null && objectProp == WrapperObjectProps.hasFieldPattern) {
		    objectProp.set(wrapper, getFieldPattern(objectNode.asNode()));
		}
	    }

	}
	return wrapper;
    }

    private FieldPattern getFieldPattern(Node node) {
	ExtendedIterator<Triple> triplesIter = this.store.execute(node, null, null);
	if (triplesIter == null) {
	    return null;
	}

	List<Triple> triples = triplesIter.toList();
	if (triples == null || triples.isEmpty()) {
	    return null;
	}

	FieldPattern fieldPattern = new FieldPattern();
	for (Triple triple : triples) {
	    // System.out.println(triple);
	    Node predicate = triple.getPredicate();
	    Node object = triple.getObject();

	    WrapperDataProps dataProp = null;
	    WrapperObjectProps objectProp = null;
	    try {
		dataProp = WrapperDataProps.valueOf(predicate.getLocalName());
	    }
	    catch (IllegalArgumentException ex) {

	    }
	    try {
		objectProp = WrapperObjectProps.valueOf(predicate.getLocalName());
	    }
	    catch (IllegalArgumentException ex) {

	    }

	    if (dataProp != null) {
		dataProp.set(fieldPattern, object.getLiteralValue().toString());
	    }
	    if (objectProp != null) {
		switch (objectProp) {
		case hasPatternExpression:
		    objectProp.set(fieldPattern, getFieldPatternExpression(object));
		    break;
		case hasPosition:
		    objectProp.set(fieldPattern, getPosition(object));
		    break;

		default:
		    break;
		}
	    }
	}

	return fieldPattern;
    }

    private PositionPattern getPosition(Node node) {
	ExtendedIterator<Triple> triplesIter = this.store.execute(node, null, null);
	if (triplesIter == null) {
	    return null;
	}

	List<Triple> triples = triplesIter.toList();
	if (triples == null || triples.isEmpty()) {
	    return null;
	}

	PositionPattern positionPattern = new PositionPattern();
	for (Triple triple : triples) {
	    // System.out.println(triple);
	    Node predicate = triple.getPredicate();
	    Node object = triple.getObject();

	    WrapperObjectProps objectProp = null;

	    try {
		objectProp = WrapperObjectProps.valueOf(predicate.getLocalName());
	    }
	    catch (IllegalArgumentException ex) {

	    }

	    if (objectProp != null) {
		switch (objectProp) {
		case hasLocalTag:
		case hasParentTag:
		case hasChildTag:
		case hasNextsiblingTag:
		case hasPresiblingTag:
		    objectProp.set(positionPattern, getTagPattern(object));
		    break;
		default:
		    break;
		}
	    }
	}

	return positionPattern;
    }

    private TagPattern getTagPattern(Node node) {
	ExtendedIterator<Triple> triplesIter = this.store.execute(node, null, null);
	if (triplesIter == null) {
	    return null;
	}

	List<Triple> triples = triplesIter.toList();
	if (triples == null || triples.isEmpty()) {
	    return null;
	}
	TagPattern tagPattern = new TagPattern();
	for (Triple triple : triples) {
	    // System.out.println(triple);
	    Node predicate = triple.getPredicate();
	    Node object = triple.getObject();

	    WrapperDataProps dataProp = null;
	    WrapperObjectProps objectProp = null;
	    try {
		dataProp = WrapperDataProps.valueOf(predicate.getLocalName());
	    }
	    catch (IllegalArgumentException ex) {

	    }
	    try {
		objectProp = WrapperObjectProps.valueOf(predicate.getLocalName());
	    }
	    catch (IllegalArgumentException ex) {

	    }

	    if (dataProp != null) {
		dataProp.set(tagPattern, object.getLiteralValue().toString());
	    }
	    if (objectProp != null && objectProp == WrapperObjectProps.hasAttribute) {
		objectProp.set(tagPattern, getAttributePattern(object));
	    }
	}

	return tagPattern;
    }

    private AttributePattern getAttributePattern(Node node) {
	ExtendedIterator<Triple> triplesIter = this.store.execute(node, null, null);
	if (triplesIter == null) {
	    return null;
	}

	List<Triple> triples = triplesIter.toList();
	if (triples == null || triples.isEmpty()) {
	    return null;
	}

	AttributePattern attributePattern = new AttributePattern();
	for (Triple triple : triples) {
	    // System.out.println(triple);
	    Node predicate = triple.getPredicate();
	    Node object = triple.getObject();

	    WrapperDataProps dataProp = null;
	    try {
		dataProp = WrapperDataProps.valueOf(predicate.getLocalName());
	    }
	    catch (IllegalArgumentException ex) {

	    }

	    if (dataProp != null) {
		dataProp.set(attributePattern, object.getLiteralValue().toString());
	    }
	}

	return attributePattern;
    }

    private FieldPatternExpression getFieldPatternExpression(Node node) {
	ExtendedIterator<Triple> triplesIter = this.store.execute(node, null, null);
	if (triplesIter == null) {
	    return null;
	}

	List<Triple> triples = triplesIter.toList();
	if (triples == null || triples.isEmpty()) {
	    return null;
	}

	FieldPatternExpression fieldPatternExpression = new FieldPatternExpression();
	for (Triple triple : triples) {
	    // System.out.println(triple);
	    Node predicate = triple.getPredicate();
	    Node object = triple.getObject();

	    WrapperDataProps dataProp = null;
	    try {
		dataProp = WrapperDataProps.valueOf(predicate.getLocalName());
	    }
	    catch (IllegalArgumentException ex) {

	    }

	    if (dataProp != null) {
		dataProp.set(fieldPatternExpression, object.getLiteralValue().toString());
	    }
	}

	return fieldPatternExpression;
    }

    public List<String> getWrapperNames() {
	List<String> retList = new ArrayList<String>();
	ExtendedIterator<Triple> execute = this.store.execute(null, TripleHelper.createWrapperNamePredicate(), null);
	while (execute.hasNext()) {
	    Triple nextTriple = execute.next();
	    if (nextTriple.getObject().isLiteral()) {
		retList.add(nextTriple.getObject().getLiteralValue().toString());
	    }
	}
	return retList;
    }

    public void removeWrapper(String wrapperName) {
	if (StringUtils.isBlank(wrapperName)) {
	    return;
	}

	List<Triple> triples = new ArrayList<Triple>();

	List<QuerySolution> executeSelectQuery = this.store.executeSelectQuery(WrapperSparqlHelper.getWrapperTriples(wrapperName));
	if (executeSelectQuery != null && !executeSelectQuery.isEmpty()) {
	    for (QuerySolution querySolution : executeSelectQuery) {
		RDFNode wrapperNode = querySolution.get("wrapper");
		RDFNode predicateNode = querySolution.get("predicate");
		RDFNode objectNode = querySolution.get("object");
		// System.out.println(wrapperNode.toString() + " " +
		// predicateNode.toString() + " " + objectNode.toString());
		if (objectNode.isAnon()) {
		    triples.addAll(removeNodes(objectNode.asNode()));
		}

		triples.add(new Triple(wrapperNode.asNode(), predicateNode.asNode(), objectNode.asNode()));
	    }
	}

	this.store.removeTriple(triples);
    }

    private List<Triple> removeNodes(Node node) {
	List<Triple> triples = new ArrayList<Triple>();
	ExtendedIterator<Triple> execute = this.store.execute(node, null, null);
	while (execute.hasNext()) {
	    Triple next = execute.next();
	    // System.out.println(next);
	    if (next.getObject().isBlank()) {
		triples.addAll(removeNodes(next.getObject()));
	    }
	    triples.add(next);
	}

	return triples;
    }

    public synchronized void updateWrapper(Wrapper wrapper) {
	if (wrapper == null || StringUtils.isBlank(wrapper.getName())) {
	    throw new SWException(ExceptionCode.InvalidInput);
	}
	String wrapperName = wrapper.getName().toLowerCase(Locale.ENGLISH);
	String base = wrapper.getBase();
	String description = wrapper.getDescription();
	String encoding = wrapper.getEncoding();

	removeWrapper(wrapperName);

	List<Triple> triples = new ArrayList<Triple>();
	Node wrapperNode = NodeFactory.createAnon();

	triples.add(new Triple(wrapperNode, TripleHelper.createTypePredicate(), TripleHelper.createWrapper()));
	triples.add(new Triple(wrapperNode, TripleHelper.createWrapperPredicate(WrapperDataProps.name), NodeFactory.createLiteral(wrapperName)));
	if (StringUtils.isNotBlank(base)) {
	    triples.add(new Triple(wrapperNode, TripleHelper.createWrapperPredicate(WrapperDataProps.base), NodeFactory.createLiteral(base)));
	}

	if (StringUtils.isNotBlank(description)) {
	    triples.add(new Triple(wrapperNode, TripleHelper.createWrapperPredicate(WrapperDataProps.description), NodeFactory.createLiteral(description)));
	}

	if (StringUtils.isNotBlank(encoding)) {
	    triples.add(new Triple(wrapperNode, TripleHelper.createWrapperPredicate(WrapperDataProps.encoding), NodeFactory.createLiteral(encoding)));
	}

	List<FieldPattern> fields = wrapper.getFields();
	if (fields != null && !fields.isEmpty()) {
	    for (FieldPattern fieldPattern : fields) {
		if (fieldPattern != null) {
		    triples.add(new Triple(wrapperNode, TripleHelper.createWrapperPredicate(WrapperObjectProps.hasFieldPattern),
			    createFieldPattern(fieldPattern)));
		}
	    }
	}
	this.store.addTriplesAsBulk(triples);

    }

    private Node createFieldPattern(FieldPattern fieldPattern) {
	List<Triple> triples = new ArrayList<Triple>();

	String fieldName = fieldPattern.getFieldName().name();
	String fieldType = fieldPattern.getFieldType().name();
	boolean multiple = fieldPattern.isMultipleResults();
	String fieldAttribute = fieldPattern.getTagAttribute();
	List<FieldPatternExpression> expressions = fieldPattern.getExpressions();
	PositionPattern positionPattern = fieldPattern.getPosition();

	Node fieldPatternNode = NodeFactory.createAnon();
	triples.add(new Triple(fieldPatternNode, TripleHelper.createWrapperPredicate(WrapperDataProps.fieldName), NodeFactory.createLiteral(fieldName)));
	triples.add(new Triple(fieldPatternNode, TripleHelper.createWrapperPredicate(WrapperDataProps.fieldType), NodeFactory.createLiteral(fieldType)));

	if (StringUtils.isNotBlank(fieldAttribute)) {
	    triples.add(new Triple(fieldPatternNode, TripleHelper.createWrapperPredicate(WrapperDataProps.fieldAttribute), NodeFactory
		    .createLiteral(fieldAttribute)));
	}

	triples.add(new Triple(fieldPatternNode, TripleHelper.createWrapperPredicate(WrapperDataProps.multipleResults), NodeFactory.createLiteral(Boolean
		.toString(multiple))));

	if (expressions != null && !expressions.isEmpty()) {
	    for (FieldPatternExpression exp : expressions) {
		triples.add(new Triple(fieldPatternNode, TripleHelper.createWrapperPredicate(WrapperObjectProps.hasPatternExpression),
			createFieldPatternExpression(exp)));
	    }
	}

	if (positionPattern != null && positionPattern.getLocalPattern() != null) {
	    triples.add(new Triple(fieldPatternNode, TripleHelper.createWrapperPredicate(WrapperObjectProps.hasPosition),
		    createPositionPattern(positionPattern)));
	}

	this.store.addTriplesAsBulk(triples);

	return fieldPatternNode;
    }

    private Node createFieldPatternExpression(FieldPatternExpression fieldPatternExpression) {
	List<Triple> triples = new ArrayList<Triple>();
	Node fieldPatternExpNode = NodeFactory.createAnon();

	String pattern = fieldPatternExpression.getPattern();
	String group = fieldPatternExpression.getGroup();
	String expType = fieldPatternExpression.getExpressionType().name();
	boolean inc = fieldPatternExpression.isInclusive();

	triples.add(new Triple(fieldPatternExpNode, TripleHelper.createWrapperPredicate(WrapperDataProps.inclusive), NodeFactory.createLiteral(Boolean
		.toString(inc))));

	if (StringUtils.isNotBlank(pattern)) {
	    triples.add(new Triple(fieldPatternExpNode, TripleHelper.createWrapperPredicate(WrapperDataProps.pattern), NodeFactory.createLiteral(pattern)));
	}

	if (StringUtils.isNotBlank(group)) {
	    triples.add(new Triple(fieldPatternExpNode, TripleHelper.createWrapperPredicate(WrapperDataProps.group), NodeFactory.createLiteral(group)));
	}

	if (StringUtils.isNotBlank(expType)) {
	    triples.add(new Triple(fieldPatternExpNode, TripleHelper.createWrapperPredicate(WrapperDataProps.expressionType), NodeFactory
		    .createLiteral(expType)));
	}

	this.store.addTriplesAsBulk(triples);
	return fieldPatternExpNode;
    }

    private Node createPositionPattern(PositionPattern positionPattern) {
	List<Triple> triples = new ArrayList<Triple>();
	Node positionNode = NodeFactory.createAnon();

	TagPattern localTag = positionPattern.getLocalPattern();
	TagPattern parentTag = positionPattern.getParentPattern();
	TagPattern childTag = positionPattern.getChildPattern();
	TagPattern nextTag = positionPattern.getNextSiblingPattern();
	TagPattern prevTag = positionPattern.getPreSiblingPattern();

	if (localTag != null) {
	    triples.add(new Triple(positionNode, TripleHelper.createWrapperPredicate(WrapperObjectProps.hasLocalTag), createTagPattern(localTag)));
	}
	if (parentTag != null) {
	    triples.add(new Triple(positionNode, TripleHelper.createWrapperPredicate(WrapperObjectProps.hasParentTag), createTagPattern(parentTag)));
	}
	if (childTag != null) {
	    triples.add(new Triple(positionNode, TripleHelper.createWrapperPredicate(WrapperObjectProps.hasChildTag), createTagPattern(childTag)));
	}
	if (nextTag != null) {
	    triples.add(new Triple(positionNode, TripleHelper.createWrapperPredicate(WrapperObjectProps.hasNextsiblingTag), createTagPattern(nextTag)));
	}
	if (prevTag != null) {
	    triples.add(new Triple(positionNode, TripleHelper.createWrapperPredicate(WrapperObjectProps.hasPresiblingTag), createTagPattern(prevTag)));
	}
	this.store.addTriplesAsBulk(triples);
	return positionNode;
    }

    private Node createTagPattern(TagPattern tagPattern) {
	List<Triple> triples = new ArrayList<Triple>();
	String tagName = tagPattern.getTagName();
	String tagValue = tagPattern.getTagValue();
	String tagExp = tagPattern.getTagExpression().name();
	boolean recursive = tagPattern.isRecursive();
	List<AttributePattern> attList = tagPattern.getAttributes();

	Node tagNode = NodeFactory.createAnon();

	triples.add(new Triple(tagNode, TripleHelper.createWrapperPredicate(WrapperDataProps.recursive), NodeFactory.createLiteral(Boolean.toString(recursive))));

	if (StringUtils.isNotBlank(tagName)) {
	    triples.add(new Triple(tagNode, TripleHelper.createWrapperPredicate(WrapperDataProps.tagName), NodeFactory.createLiteral(tagName)));
	}

	if (StringUtils.isNotBlank(tagValue)) {
	    triples.add(new Triple(tagNode, TripleHelper.createWrapperPredicate(WrapperDataProps.tagValue), NodeFactory.createLiteral(tagValue)));
	}

	triples.add(new Triple(tagNode, TripleHelper.createWrapperPredicate(WrapperDataProps.tagExpression), NodeFactory.createLiteral(tagExp)));

	if (attList != null && !attList.isEmpty()) {
	    for (AttributePattern attPattern : attList) {
		if (StringUtils.isNotBlank(attPattern.getAttributeName())) {
		    triples.add(new Triple(tagNode, TripleHelper.createWrapperPredicate(WrapperObjectProps.hasAttribute), createAttributePattern(attPattern)));
		}
	    }
	}

	this.store.addTriplesAsBulk(triples);
	return tagNode;
    }

    private Node createAttributePattern(AttributePattern attributePattern) {
	List<Triple> triples = new ArrayList<Triple>();
	Node attributeNode = NodeFactory.createAnon();

	String attName = attributePattern.getAttributeName();
	String attValue = attributePattern.getAttributeValue();
	String attExp = attributePattern.getAttributeExpression().name();

	if (StringUtils.isNotBlank(attName)) {
	    triples.add(new Triple(attributeNode, TripleHelper.createWrapperPredicate(WrapperDataProps.attributeName), NodeFactory.createLiteral(attName)));
	}
	if (StringUtils.isNotBlank(attValue)) {
	    triples.add(new Triple(attributeNode, TripleHelper.createWrapperPredicate(WrapperDataProps.attributeValue), NodeFactory.createLiteral(attValue)));
	}
	triples.add(new Triple(attributeNode, TripleHelper.createWrapperPredicate(WrapperDataProps.attributeExpression), NodeFactory.createLiteral(attExp)));
	this.store.addTriplesAsBulk(triples);
	return attributeNode;
    }

}
